/**
Metamorfose is an interactive ecosystem, which uses computer vision
and sound analysis to define its basic rules.
Copyright (C) 2008  laboratorio (info@laboratorio.us)

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

class Ameba {
  
  Vector3D location;
  float angle;
  Sprite shape;
  Animation animation;
  boolean isDying, reloading;
  float mass;
  float G;
  SampleController noiseTrigger;
  
  /**
   * Class constructor
   *
   * @param _location a vector indicating the initial position of the object
   * @param _m the mass of this object
   * @param _gravity gravitational constant
   */
  Ameba(Vector3D _location, float _m, float _gravity) {
    this.location = _location;
    this.mass = _m;
    this.animation = randomAnimation();
    this.shape = new Sprite(animation.birthFrames, false);
    this.isDying = false;
    this.reloading = false;   
    G = _gravity;
    noiseTrigger = new SampleController(sampleLib);
  }

  /**
   * Gets the location of the current object
   *
   * @return a position vector
   */
  Vector3D getLocation() {
    return this.location; 
  }

  /**
   * Updates object's position in cartesian space
   *
   * @param _location the position vector
   * @param _angle rotation of this object
   */
  void update(Vector3D _location, float _angle) {
    location = _location;
    angle = _angle;
    noiseTrigger.tryToPlay(_location);
  }

  /**
   * Prepares the object die changing its shape and stopping the loop
   */
  void die() {
    this.shape.frames = animation.dieFrames;
    this.shape.looped = false;
    this.shape.reloadFrames();
    this.isDying = true;
  }

  /**
   * Reloads the object
   */
  void reload() {
    this.shape.frames = animation.dieFrames;
    this.shape.looped = false;
    this.shape.reloadFrames();
    this.reloading = true;
  }

  /**
   * Puts the object in a born state
   * 
   * @see Animation
   */
  void resetAnimation()
  {
    this.animation = randomAnimation();
    this.shape.frames = animation.birthFrames;
    this.shape.looped = false;
    this.shape.reloadFrames();
    this.reloading = false;
  }

  /**
   * Displays the object
   */
  void render() {
    pushMatrix();
    translate(location.x, location.y);
    rotate(angle);
    this.shape.display();
    popMatrix();
    if (this.wasBorn() && !isDying && !reloading) {
      this.shape.frames = animation.loopFrames;
      this.shape.looped = true;
      this.shape.reloadFrames();
    }
  }

  /**
   * Calculates the gravitational force.
   * Based on Daniel Shiffman experiments on forces
   * 
   * @param p 
   * @see Parasite
   * @return a direction vector 
   */
  Vector3D calcGravForce(Parasite p) {
    Vector3D dir = Vector3D.sub(location, p.getLoc());
    float d = dir.magnitude();
    d = constrain(d, 230.0f, 280.0f);
    dir.normalize();
    float force = (G * mass * p.getMass()) / (d * d);
    dir.mult(force);
    return dir;
  }

  /**
   * Selects a random animation to be the shape of this object
   *
   * @return an animation instance
   * @see Animation
   */
  Animation randomAnimation() {
    return animations[int(random(animations.length))];
  } 

  /**
   * Checks if this object has just born
   */
  private boolean wasBorn()
  {
    return this.shape.reachLastFrame();
  }
  
  /**
   * Checks the existence of the object
   */  
  boolean isDead()
  {
    return (this.shape.reachLastFrame() && this.isDying) ? true : false;
  }
  
  /**
   * Checks if the reload sequence has ended
   */  
  boolean reloadComplete()
  {
    return (this.shape.reachLastFrame() && this.reloading) ? true : false;
  } 
}